#include "mini.kit/text.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <iconv.h>
#include <algorithm>

namespace MINIKIT
{

#define MSB4(byte) (((unsigned char)byte) >> 4)
#define LSB4(byte) (((unsigned char)byte) & 0x0F )

char Text::c2x(unsigned char c)
{
	if ( c >= 16) return 'F';
	return ( (c < 10) ? ('0' +  c) : ( 'A' + c - 10));
}

unsigned char Text::x2c(const char *what)
{
	char digit;
	digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
	digit *= 16;
	digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
	return(digit);
}

void Text::upper(char* sz)
{
    for(uint32_t i=0; i<strlen(sz); i++)
        sz[i] = ::toupper(sz[i]);
}

void Text::upper(std::string& s)
{
    for(uint32_t i=0; i<s.length(); i++)
        s[i] = ::toupper(s[i]);
}

void Text::lower(char* sz)
{
    for(uint32_t i=0; i<strlen(sz); i++)
        sz[i] = ::tolower(sz[i]);
}

void Text::lower(std::string& s)
{
    for(uint32_t i=0; i<s.length(); i++)
        s[i] = ::tolower(s[i]);
}

size_t Text::split(const std::string &s, const std::string &sep, std::vector<std::string> &out_result)
{
        out_result.clear();
        if( s.empty() )
        {
                return 0;
        }
        size_t idx_end=0, idx_start = 0;
        while (idx_end != std::string::npos)
        {
                idx_end = s.find(sep, idx_start);
                if (idx_end == std::string::npos)
                {
                        out_result.push_back(s.substr(idx_start));
                        break;
                }

                out_result.push_back(s.substr(idx_start, idx_end - idx_start));
                idx_start = idx_end + sep.length();
        }
	return out_result.size();
}

size_t Text::replace(std::string& src, const char* token, const char* symbol)
{
	if(NULL==token||NULL==symbol) return 0;
	size_t iTokenLen = strlen(token);
	size_t iSymbolLen=strlen(symbol);
	size_t iCount = 0;
	std::string::size_type idx = src.find(token);
	while( idx!=std::string::npos && iTokenLen>0 )
	{
		src.replace(idx,iTokenLen,symbol);
		idx = src.find(token, (idx+iSymbolLen) );
		iCount ++;
	}
	return iCount;
}

std::string Text::trimL(std::string &str, char c)
{
        int pos=0;
	if( '\0'==c )
        	while( str[pos]==' ' || str[pos]=='\t' ) { pos++; }
	else
        	while( str[pos]==c ) { pos++; }

        return str=str.substr(pos);
}

std::string Text::trimR(std::string &str, char c)
{
        int pos=str.length()-1;
	if( '\0'==c )
        	while( str[pos]==' ' || str[pos]=='\t' ) { pos--; }
	else
        	while( str[pos]==c ) { pos--; }

        return str=str.substr(0, pos+1);
}

std::string Text::trim(std::string &str, char c)
{
        trimL(str, c);
        return trimR(str, c);
}

void Text::conv_gbk_utf8(std::string &s)
{
	std::string sTmp;
	sTmp.resize( s.size()*2+1 );
	int32_t ret = conv( "GBK", "UTF8", (char*)s.c_str(), s.size(), &sTmp[0], sTmp.size() );
        if( ret > 0 )
	{
		sTmp.resize(ret);
                s = sTmp;
	}
}

void Text::conv_utf8_gbk(std::string &s)
{
	std::string sTmp;
	sTmp.resize( s.size()+1 );
	int32_t ret = conv( "UTF8", "GBK", (char*)s.c_str(), s.size(), &sTmp[0], sTmp.size() );
        if( ret >0 )
	{
		sTmp.resize(ret);
                s = sTmp;
	}
}

int32_t Text::conv(const char *szCharsetFrom, const char *szCharsetTo, char *szIn, size_t lenIn, char *szOut, size_t lenOut)
{
    iconv_t confd;
    char *pin = const_cast<char*>(szIn);
    char *pout = szOut;

    confd = ::iconv_open(szCharsetTo, szCharsetFrom);
    if( confd == (iconv_t)-1 )
    {
	//fprintf(stderr, "failed to iconv_open(%s,%s).\n");
        return -1;
    }

    memset(szOut, 0, lenOut);
    if( ::iconv(confd, &pin, &lenIn, &pout, &lenOut) == (size_t)-1 )
    {
	//fprintf(stderr, "failed to iconv.\n");
        ::iconv_close(confd);
        return -1;
    }

    ::iconv_close(confd);
    //fprintf(stderr, "iconv-len: %d, %s.\n", (pout-szOut), szOut);
    return pout-szOut;

}

void Text::unescape(const std::string &s, std::string &s_out)
{
	s_out = s;
	MINIKIT::Text::unescape( &s_out[0] );
	s_out.resize( strlen(&s_out[0]) );
}

void Text::unescape(char* sz)
{
	char *x, *y;

	y = ::strchr(sz, '%');
	if (y == NULL)
	{
		return;
	}

	for (x = y; *y; ++x, ++y)
	{
		if (*y != '%')
		{
			*x = *y;
		}
		else
		{
			if (!::isxdigit(*(y + 1)) || !::isxdigit(*(y + 2)))
			{
				*x = '%';
			}
			else
			{
				char decoded;
				decoded = x2c(y + 1);
				*x = decoded;
				y += 2;
			}
		}
	}
	*x = '\0';
}

void Text::escape(const std::string &s, std::string &s_out)
{
	int32_t iEncodeLen = s.size() * 3 + 1;
	char *pszEncoded = new char[iEncodeLen];
	escape(s.c_str(), pszEncoded, iEncodeLen);
	s_out = pszEncoded;
	delete [] pszEncoded;
}

int32_t Text::escape(const char *pszStr, char * pszResult, uint32_t iOutLen)
{
	static int8_t wontEncode[128] = { // 16 * 8
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0
	};

	int32_t iLen = ::strlen(pszStr);
	if ( iOutLen < 3 * iLen + 1)
		return -1;

	int32_t idx = 0,  idxResult = 0;
	int32_t val = 0;
	for ( idx = 0; idx < iLen && idxResult <iOutLen - 3;++idx)
	{
		val = (unsigned char )pszStr[idx];
		if ( val < 128 && wontEncode[val]){
			pszResult[idxResult] = val;
			idxResult += 1;
		}
		else{ // non-ASCII
			pszResult[idxResult] = '%';
			pszResult[idxResult + 1] = c2x(MSB4(val));
			pszResult[idxResult + 2] = c2x(LSB4(val));
			idxResult += 3;
		}
	}

	pszResult[idxResult] = '\0';
	if ( idx != iLen ) //not complete
		return -1;

	return idxResult;
}

#undef MSB4
#undef LSB4


size_t Text::sprintf(std::string& s_out, const char* fmt, ...)
{
	#define CONST_PRE_DEF_SIZE 256
	int32_t isize = 0;
	va_list args;
	va_start( args, fmt);
		s_out.resize( CONST_PRE_DEF_SIZE );
		isize = ::vsnprintf((char*)s_out.c_str(), CONST_PRE_DEF_SIZE, fmt, args);
		if( isize>=CONST_PRE_DEF_SIZE )
			s_out.resize(isize+1);//including '\0'
		else if( isize>=0 && isize<CONST_PRE_DEF_SIZE )
			s_out.resize(isize);
	va_end( args );
	if( isize<0 ) 
		return 0;
	if( isize>=0 && isize<CONST_PRE_DEF_SIZE )
		return (size_t)isize;

	va_start( args, fmt);
		////the following 2 steps could be dangerous,
		////since the memory allocation for std::string doesn't have to be continuous.
		////use std::vector<char> instead.
		//memset( (char*)s_out.c_str(), 0x00, isize );
		::vsnprintf( (char*)s_out.c_str(), s_out.size(), fmt, args );
		//s_out.resize(strlen(s_out.c_str()));
	va_end( args );
	return (size_t)isize;
	#undef CONST_PRE_DEF_SIZE
}

/*
uint32_t getVarLength( const char* fmt, ... )
{
	int32_t isize= 0;
	va_list args;
	va_start( args, fmt);
		char tmp[16]={0};
		isize = vsnprintf(tmp, 0, fmt, args) + 1;//including '\0'
		if ( isize<0 )
			isize = 0;
	va_end( args );
	return (size_t)isize; 

}
*/



uint32_t Http::ParseHeader(const std::string &header_string, std::map<std::string, std::string>& out_fields, const std::string &namelist )
{
	if ( header_string.empty() )
		return 0;

	out_fields.clear();
	std::string sNameList  = namelist;
	MINIKIT::Text::upper( sNameList );

	std::string sHeader    = header_string;
	if( sHeader.find("\r\n") == std::string::npos )
		sHeader += "\r\n";

	std::string sFirstLine = sHeader.substr(0, sHeader.find("\r\n"));

	std::string::size_type paramStartPos = 0;
	std::string::size_type paramEndPos   = sFirstLine.find( " " );
	out_fields["HTTP_METHOD"] = sFirstLine.substr(paramStartPos, paramEndPos-paramStartPos);
	paramStartPos = sFirstLine.find( "/", paramEndPos+1 );
	paramEndPos   = sFirstLine.find( " ", paramStartPos );
	out_fields["HTTP_RAWPATH"] = sFirstLine.substr(paramStartPos, paramEndPos-paramStartPos);
	paramStartPos = sFirstLine.find( "HTTP", paramEndPos+1 );
	out_fields["HTTP_VERSION"] = sFirstLine.substr(paramStartPos, sFirstLine.size()-paramStartPos);


	paramStartPos = 0;
	paramEndPos   = out_fields["HTTP_RAWPATH"].find( "?", 0 );
	if( std::string::npos != paramEndPos )
	{
		out_fields["HTTP_PATH"]        = out_fields["HTTP_RAWPATH"].substr( paramStartPos, (paramEndPos-paramStartPos) );
		paramStartPos = paramEndPos + 1;
		paramEndPos   = out_fields["HTTP_RAWPATH"].size();
		out_fields["HTTP_QUERYSTRING"] = out_fields["HTTP_RAWPATH"].substr( paramStartPos, (paramEndPos-paramStartPos) );
	}
	else
	{
		out_fields["HTTP_PATH"] = out_fields["HTTP_RAWPATH"];
	}


	std::string sParam;
	std::string::size_type valuePos;
	paramStartPos = 0;
	paramEndPos   = std::string::npos;

	while ( (paramEndPos = sHeader.find("\r\n", paramStartPos)) != std::string::npos )
	{
		sParam = sHeader.substr(paramStartPos, paramEndPos-paramStartPos);
		paramStartPos = paramEndPos+2;
		if ( (valuePos = sParam.find(":")) == std::string::npos)
			continue;//not a legal name-value pair

		std::string sName = sParam.substr(0, valuePos);
		MINIKIT::Text::trim(sName);
		MINIKIT::Text::upper(sName);
		if ( !sNameList.empty() && std::string::npos==sNameList.find(sName) )
			continue;

		std::string sValue;
		MINIKIT::Text::unescape(sParam.substr(valuePos+1), sValue);
		MINIKIT::Text::trim(sValue);
		out_fields[sName] = sValue;
	}
	return out_fields.size();
}


uint32_t Http::ParseQueryString(const std::string &query_string, std::map<std::string, std::string>& out_params, const std::string &namelist)
{
	std::string::size_type paramStartPos = query_string.find("?");
	std::string::size_type paramEndPos;
	std::string::size_type valuePos;
	std::string sRaw = query_string+"&";
	std::string sParam;
	out_params.clear();

	if( paramStartPos!=std::string::npos && paramStartPos<sRaw.size()-1 )
		paramStartPos ++;
	else
		paramStartPos = 0;

	while ( (paramEndPos = sRaw.find("&", paramStartPos)) != std::string::npos )
	{
		sParam = sRaw.substr(paramStartPos, paramEndPos-paramStartPos);
		paramStartPos = paramEndPos+1;
		if ( (valuePos = sParam.find("=")) == std::string::npos)
			continue;//not a legal name-value pair

		std::string sName = sParam.substr(0, valuePos);
		if ( !namelist.empty() && std::string::npos==namelist.find(sName) )
			continue;

		std::string sValue;
		MINIKIT::Text::unescape(sParam.substr(valuePos+1), sValue);
		out_params[sName] = sValue;
	}
	return out_params.size();
}

uint32_t Http::ParseCookie(const std::string& cookie_string, std::map<std::string, std::string>& out_cookies, const std::string &namelist)
{
	if ( cookie_string.empty() )
		return 0;
	out_cookies.clear();
	std::string sCookie = cookie_string+";";
	sCookie.erase(std::remove(sCookie.begin(), sCookie.end(), ' '), sCookie.end());

	std::string::size_type paramStartPos = 0;
	std::string::size_type paramEndPos;
	std::string sParam;
	std::string::size_type valuePos;

	while ( (paramEndPos = sCookie.find(";", paramStartPos)) != std::string::npos )
	{
		sParam = sCookie.substr(paramStartPos, paramEndPos-paramStartPos);
		paramStartPos = paramEndPos+1;
		if ( (valuePos = sParam.find("=")) == std::string::npos)
			continue;//not a legal name-value pair

		std::string sName = sParam.substr(0, valuePos);
		if ( !namelist.empty() && std::string::npos==namelist.find(sName) )
			continue;

		std::string sValue;
		MINIKIT::Text::unescape(sParam.substr(valuePos+1), sValue);
		out_cookies[sName] = sValue;
	}
	return out_cookies.size();
}


}; //namespace MINIKIT

